package com.jnc.core.config.shiro.session;

import cn.hutool.core.collection.CollectionUtil;
import com.jnc.common.constant.ShiroConstant;
import com.jnc.common.constant.SysConstant;
import com.jnc.common.util.convert.SerializeUtil;
import com.jnc.core.config.redis.RedisStore;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * @Author: jjn
 * @Date: 2018/10/31
 * @Desc:
 */
@Slf4j
public class RedisShiroCache<K, V> implements Cache<K, V> {
    
    private RedisStore redisStore;

    public RedisShiroCache(RedisStore redisStore) {
        this.redisStore = redisStore;
    }

    /**
     * 获取shiro缓存
     * @param k
     * @return
     * @throws CacheException
     */
    @SuppressWarnings("unchecked")
    @Override
    public V get(K k) throws CacheException {
        if(log.isDebugEnabled()){
            log.debug("RedisShiroCache get key: {}", k);
        }
        if(null == k){
            return null;
        }
        byte[] value = redisStore.get(getByteKey(k));
        return (V) SerializeUtil.deserialize(value);
    }

    /**
     * 设置Shiro缓存
     * @param k
     * @param v
     * @return
     * @throws CacheException
     */
    @Override
    public V put(K k, V v) throws CacheException {
        if(log.isDebugEnabled()){
            log.debug("RedisShiroCache put key: {}, value: {}", k, v);
        }
        if(k == null || v == null){
            return null;
        }
        redisStore.setExpire(getByteKey(k), SerializeUtil.serialize(v), ShiroConstant.EXPIRE_SECONDS);
        return v;
    }

    /**
     * 移除Shiro缓存
     * @param k
     * @return
     * @throws CacheException
     */
    @SuppressWarnings("unchecked")
    @Override
    public V remove(K k) throws CacheException {
        if(log.isDebugEnabled()){
            log.debug("RedisShiroCache remove key: {}", k);
        }
        if(null == k){
            return null;
        }
        byte[] value = redisStore.get(getByteKey(k));
        redisStore.del(getByteKey(k));
        return (V) SerializeUtil.deserialize(value);
    }

    /**
     * 清除所有Shiro缓存
     * @throws CacheException
     */
    @Override
    public void clear() throws CacheException {
        if(log.isDebugEnabled()){
            log.debug("RedisShiroCache clear all shiro cache");
        }
        Set<String> keysSet = redisStore.keys(ShiroConstant.SHIRO_CACHE_PREFIX);
        if(CollectionUtil.isNotEmpty(keysSet)){
            Iterator<String> it = keysSet.iterator();
            while (it.hasNext()){
                String next = it.next();
                redisStore.del(next);
            }
        }
    }

    /**
     * 获取Shiro缓存个数
     * @return
     */
    @Override
    public int size() {
        if(log.isDebugEnabled()){
            log.debug("RedisShiroCache get shiro cache count");
        }
        Set<String> keysSet = redisStore.keys(ShiroConstant.SHIRO_CACHE_PREFIX);
        if(keysSet != null && keysSet.size() > 0){
            return keysSet.size();
        }
        return 0;
    }

    /**
     * 获取所有的key
     * @return
     */
    @SuppressWarnings("unchecked")
    @Override
    public Set<K> keys() {
        if(log.isDebugEnabled()){
            log.debug("RedisShiroCache get all shiro cache");
        }
        Set<String> keySet = redisStore.keys(ShiroConstant.SHIRO_CACHE_PREFIX);
        Set<K> result = CollectionUtil.newHashSet();

        if (CollectionUtils.isEmpty(keySet)) {
            return Collections.emptySet();
        }

        for (String key : keySet) {
            result.add((K) key);
        }
        return result;
    }

    /**
     * 获取所有Shiro缓存的value
     * @return
     */
    @SuppressWarnings("unchecked")
    @Override
    public Collection<V> values() {
        Set<K> keys = keys();
        List<V> values = new ArrayList<>(keys.size());
        for (K k : keys){
            values.add((V) SerializeUtil.deserialize(redisStore.get(getByteKey(k))));
        }
        return values;
    }

    private byte[] getByteKey(K key){
        String preKey = ShiroConstant.SHIRO_CACHE_PREFIX + key;
        return preKey.getBytes();
    }
}
